// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;

namespace SnapObjects.Data
{
    internal class DynamicModelMeta : IDynamicModelMeta
    {
        private readonly IReadOnlyDictionary<string, IPropertyMeta> _properyMetaMap;
       
        public DynamicModelMeta(IDictionary<string, Type> properties)
        {
            var propertyList = new List<IPropertyMeta>();
            var propertyMetas = new Dictionary<string, IDynamicProperty>(StringComparer.OrdinalIgnoreCase);

            var index = 0;

            foreach (var property in properties)
            {
                var meta = this.CreatePropertyMeta(index++, property.Key, property.Value);

                propertyList.Add(meta);

                propertyMetas.Add(property.Key, meta);
            }

            this.Properties = propertyList;

            this.PropertyMap = propertyMetas;

            this.UpdatableProperties = this.PropertyMap.Values.ToList();

            _properyMetaMap = this.PropertyMap.ToDictionary(m => m.Key, y => (IPropertyMeta)y.Value);
        }


        public DynamicModelMeta(IDictionary<string, DbColumnInfo> properties)
        {
            var propertyList = new List<IPropertyMeta>();
            var propertyMetas = new Dictionary<string, IDynamicProperty>(StringComparer.OrdinalIgnoreCase);

            var columns = properties.Select(m => m.Value).ToList();

            var tables = columns
                .Select(m => (m.TableSchema ?? String.Empty) + "." + (m.Table ?? String.Empty))
                .Distinct()
                .ToList();

            var updateTable = tables.FirstOrDefault(m => !m.Equals("."))?.TrimStart('.');

            if (!String.IsNullOrWhiteSpace(updateTable) &&
                (tables.Count == 1 || (tables.Count == 2 && tables.Contains("."))))
            {
                this.MappedTable = updateTable;
            }

            foreach (var property in properties)
            {
                var meta = this.CreatePropertyMeta(property.Key, property.Value);

                propertyList.Add(meta);

                propertyMetas.Add(property.Key, meta);
            }

            this.Properties = propertyList;

            this.PropertyMap = propertyMetas;

            this.UpdatableProperties = this.PropertyMap.Values.ToList();

            _properyMetaMap = this.PropertyMap.ToDictionary(m => m.Key, y => (IPropertyMeta)y.Value);
        }

        public int PropertyCount => this.Properties.Count;
        public Type ModelType { get; } = typeof(DynamicModel);
        public IReadOnlyList<IPropertyMeta> Properties { get; }
        public IReadOnlyDictionary<string, IDynamicProperty> PropertyMap { get; }

        public IReadOnlyList<IDynamicProperty> KeyProperties
            => this.PropertyMap.Values.Where(m => m.IsKey).ToList();

        public virtual IReadOnlyList<IUpdatableProperty> UpdatableProperties { get; }

        IReadOnlyList<IPropertyMeta> IModelMeta.KeyProperties => this.KeyProperties;

        IReadOnlyList<IUpdatableProperty> IModelMeta.UpdatableProperties => this.UpdatableProperties;

        IReadOnlyDictionary<string, IPropertyMeta> IModelMeta.PropertyMap => _properyMetaMap;

        public string MappedTable { get; protected set; }

        public string MappedTableAlias { get; protected set; }

        public string MappedSchema { get; protected set; }

        public UpdateWhereStrategy UpdateWhereStrategy { get; protected set; }

        public int IndexOf(string name)
        {
            if (this.PropertyMap.TryGetValue(name, out var property))
            {
                return property.Index;
            }
            else
            {
                throw new NotPropertyException(name);
            }
        }

        public string NameOf(int index)
        {
            return this.Properties.Count > index
                ? this.Properties[index].Name : String.Empty;
        }

        public bool Contains(string name)
        {
            return this.PropertyMap.ContainsKey(name);
        }

        protected virtual DynamicPropertyMeta CreatePropertyMeta(
            int index, string name, Type type)
        {
            return new DynamicPropertyMeta(index, name, type);
        }

        protected virtual DynamicPropertyMeta CreatePropertyMeta(string propertyname, DbColumnInfo columnSchema)
        {
            return new DynamicPropertyMeta(propertyname, columnSchema);
        }
    }
}
